from os import path
from tkFileDialog import askopenfilename
from functools import partial

from nipponsteel import Tape
from nipponsteel.utils import FunctionProxy


NO_TAPE = "No tape loaded"
PLAYING = "Playing"
BACKING = "Playing back"
PAUSED  = "Paused"


class ManagerProxy(FunctionProxy):
    """This class will function as a proxy for the Manager object to other widgets (controls and 
    menus), so that those widgets can bind events to the manager before actually having a 
    reference to it. The reference will be later added with a single
        widget.manager.target = self
    done by the Manager object (see Manager.__init__()), and the methods which were previously 
    bound will now work when called."""
    
    
class Manager(object):
    """This class manages the application. It connects widgets to the current state of the tape, 
    controls playback, speed, zoom, etc."""
    def __init__(self, widgets):
        self.widgets = widgets  # this object provides access to all widgets in the app
        self.tape = None        # the Tape object being shown
        self.state = None       # one of the state constants defined above
        self.set_state(NO_TAPE, "Press Ctrl+o to open a tape file")
        
        # Connect widgets to the manager
        widgets.menus.manager.target = self
        widgets.controls.manager.target = self
        widgets.progress.command = self.set_progress
        widgets.progress.user_setable = False
        
    def setup(self, tape):
        if self.tape is not None:
            self.clear()
        self.tape = tape
        self.tape.rewind()
        self.set_state(PAUSED, "Tape loaded")
        # +++
        self.widgets.player.setup(tape)
        self.widgets.progress.set(0.0)
        self.widgets.progress.user_setable = True
        
    def clear(self):
        if self.tape is None:
            return
        self.pause()
        self.tape = None
        self.set_state(NO_TAPE, "Press Ctrl+o to open a tape file")
        # +++
        self.widgets.player.teardown()
        self.widgets.progress.set(0.0)
        self.widgets.progress.user_setable = False
        
    def set_state(self, state, info=None):
        self.state = state
        self.widgets.status.status.set(state)
        if info is not None:
            self.widgets.status.info.set(info)
            
    def set_progress(self, value):
        self.pause()
        with self.widgets.player.cleared():
            self.tape.seek(value)
            move = "Beginning of tape" if self.tape.unstarted else self.tape.previous
            self.widgets.status.info.set("[%d relocations] %s" % (self.tape.relocations, move))
            self.widgets.progress.set(self.tape.progress)
            
    # FILE MENU COMMANDS ------------------------
    @ManagerProxy.include
    def open(self, event=None):
        print "open:",
        self.pause()
        filepath = askopenfilename(parent=self.widgets.player, 
                                   title="Please select a tape file", 
                                   initialdir="./")
        if filepath:
            filepath = path.abspath(filepath)
            print "loading %s..." % filepath,
            tape = Tape.load(filepath)
            self.setup(tape)
        else:
            print "no filepath provided.",
        print "done"
        
    @ManagerProxy.include
    def close(self, event=None):
        if self.state is not NO_TAPE:
            print "close"
            self.clear()
            
    # PLAYBACK COMMANDS -------------------------
    @ManagerProxy.include
    def pause(self, event=None):
        if self.tape is not None and self.state is not PAUSED:
            print "pause"
            self.set_state(PAUSED)
            
    @ManagerProxy.include
    def play(self, event=None): 
        if self.tape is not None and self.state is not PLAYING:
            print "play"
            self.set_state(PLAYING)
            self._step()
            
    @ManagerProxy.include
    def step(self, event=None): 
        if self.tape is not None:
            print "step"
            if self.state is not PAUSED:
                self.pause()
            self._step()
            
    def _step(self):
        if self.tape.finished:
            self.pause()
        else:
            move = self.tape.step()
            chain_fnc = self._step if self.state is PLAYING else None
            self.widgets.player.display(move, at_end=chain_fnc)
            self.widgets.status.info.set("[%d relocations] %s" % (self.tape.relocations, move))
            self.widgets.progress.set(self.tape.progress)
            
    @ManagerProxy.include
    def play_back(self, event=None): 
        if self.tape is not None and self.state is not BACKING:
            print "play_back"
            self.set_state(BACKING)
            self._step_back()
            
    @ManagerProxy.include
    def step_back(self, event=None):
        if self.tape is not None:
            print "step_back"
            if self.state is not PAUSED:
                self.pause()
            self._step_back()
            
    def _step_back(self):
        if self.tape.unstarted:
            self.pause()
        else:
            move = self.tape.step_back()
            chain_fnc = self._step_back if self.state is BACKING else None
            self.widgets.player.display(move.reverse(), at_end=chain_fnc)
            self.widgets.status.info.set("[%d relocations] %s" % (self.tape.relocations, move))
            self.widgets.progress.set(self.tape.progress)
            
    @ManagerProxy.include
    def forward(self, event=None):
        if self.tape is not None:
            print "forward"
            self.set_progress(1.0)
            
    @ManagerProxy.include
    def rewind(self, event=None): 
        if self.tape is not None:
            print "rewind"
            self.set_progress(0.0)
            
    @ManagerProxy.include
    def refresh(self, event=None):
        print "refresh"
        player = self.widgets.player
        player.clear()
        player.draw()
        
    # SPEED AND ZOOM COMMANDS -------------------
    @ManagerProxy.include
    def speed_up(self, event=None): 
        player = self.widgets.player
        player.set_speed(player.speed * 2.0)
        
    @ManagerProxy.include
    def speed_down(self, event=None): 
        player = self.widgets.player
        player.set_speed(player.speed * 0.5)
        
    @ManagerProxy.include
    def speed_reset(self, event=None):
        player = self.widgets.player
        player.set_speed(1.0)
        
    @ManagerProxy.include
    def zoom_in(self, event=None):
        player = self.widgets.player
        player.set_scale(player.scale + 1.0)
        
    @ManagerProxy.include
    def zoom_out(self, event=None):
        player = self.widgets.player
        player.set_scale(player.scale - 1.0)
        
    @ManagerProxy.include
    def zoom_reset(self, event=None):
        player = self.widgets.player
        player.set_scale(1.0)
        
    @ManagerProxy.include
    def stack_size_up(self, event=None): 
        player = self.widgets.player
        player.set_stack_size(player.stack_size + 0.5)
        
    @ManagerProxy.include
    def stack_size_down(self, event=None): 
        player = self.widgets.player
        player.set_stack_size(player.stack_size - 0.5)
        
    @ManagerProxy.include
    def stack_size_reset(self, event=None):
        player = self.widgets.player
        player.set_stack_size(3.0)
        
